#!/usr/bin/python3
# -*- coding: UTF-8 -*-
from multiprocessing import Pool
from multiprocessing import Process
import time
import os
# 1、os.fork()创建进程
# ret = os.fork()
# if ret == 0:
#     # 子进程
#     print("Sub process pid = %d, Sub process ppid = %d" %
#           (os.getpid(), os.getppid()))
# else:
#     # 父进程
#     print("Parent Process ret = %d" % ret)
#     print("Parent Process pid = %d" % os.getpid())
# 注意：os.pork()函数是Unix或Linux操作系统提供的一个fork系统调用。而在Windows下会出错。所以，不要在Windows系统中使用os.fork()函数实现多进程，而要使用 python 的 multiprocessing 模块来创建多进程。multiprocessing模块就是跨平台版本的多进程模块。

# 2、使用multiprocessing模块创建进程
# Process([group[,target[,name[,args[,kwargs]]]]])，其中，group：参数未使用，值始终是None
# target：表示当前进程启动时执行的可调用对象
# name：为当前进程实例的别名
# args：表示传递给target函数的参数元组
# kwargs：表示传递给target函数的参数字典

# Process的实例p常用的方法除start()外，还有如下常用方法：
# is_alive():判断进程实例是否还在执行
# join([timeout]):是否等待进程实例执行结束，或等待多少秒
# start():启动进程实例（创建子进程）
# run():如果没有给定target参数，对这个对象调用start()方法时，就将执行对象中的run()方法
# terminate():不管任务是否完成，立即终止

# Process类还有如下常用属性：
# name:当前进程实例别名，默认为Process-N,N为从1开始递增的整数
# pid:当前进程实例的PID值


# 两个子进程将会调用的两个方法
def child_1(interval):
    print("子进程（%s）开始执行，父进程为（%s）" % (os.getpid(), os.getppid()))
    # 计时开始
    t_start = time.time()
    # 程序将会被挂起interval秒
    time.sleep(interval)
    # 计时结束
    t_end = time.time()
    print("子进程（%s）执行时间为'%0.2f'秒" % (os.getpid(), t_end - t_start))


def child_2(interval):
    print("子进程（%s）开始执行，父进程为（%s）" % (os.getpid(), os.getppid()))
    # 计时开始
    t_start = time.time()
    # 程序将会被挂起interval秒
    time.sleep(interval)
    # 计时结束
    t_end = time.time()
    print("子进程（%s）执行时间为'%0.2f'秒" % (os.getpid(), t_end - t_start))


if __name__ == '__main__':
    print("------父进程开始执行-------")
    # 输出当前程序的ID
    print("父进程PID：%s" % os.getpid())
    # 实例化进程
    p1 = Process(target=child_1, args=(1,))
    p2 = Process(target=child_2, name="mrsoft", args=(2,))
    # 启动进程p1
    p1.start()
    p2.start()
    # 同时父进程仍然往下执行，如果p2进程还在执行，将会返回True
    print("p1.is_alive=%s" % p1.is_alive())
    print("p2.is_alive=%s" % p2.is_alive())
    # 输出p1和p2进程的别名和PID
    print("p1.name=%s" % p1.name)
    print("p1.pid=%s" % p1.pid)
    print("p2.name=%s" % p2.name)
    print("p2.pid=%s" % p2.pid)
    print("------等待子进程-------")
    # 等待p进程结束
    p1.join()
    p2.join()
    print("------父进程执行结束-------")

# 3、使用Process子类创建进程
# 对于一些简单的小任务，通常使用Process(target=test)方式实现多进程。但如果要处理复杂任务的进程，通常定义一个类，使其继承Process类，下面是通过使用Process子类创建多个进程。


class SubProcess(Process):
    # 由于Process类本身也有__init__初始化方法，这个子类相当于重写了父类的这个方法
    def __init__(self, interval, name=''):
        # 调用Process父类的初始化方法
        Process.__init__(self)
        self.interval = interval
        # 判断传递的参数name是否存在
        if name:
            # 如果传递参数name,则为子进程创建name属性，否则使用默认属性
            self.name = name

    def run(self):
        print("子进程(%s) 开始执行，父进程为（%s）" % (os.getpid(), os.getppid()))
        t_start = time.time()
        time.sleep(self.interval)
        t_stop = time.time()
        print("子进程(%s)执行结束，耗时%0.2f秒" % (os.getpid(), t_stop-t_start))


if __name__ == '__main__':
    print('----------父进程开始执行----------')
    # 输出当前程序的ID
    print("父进程PID：%s" % os.getpid())
    p1 = SubProcess(interval=1, name='mrsoft')
    p2 = SubProcess(interval=2)
    # 对一个不包含target属性的Process类执行start()方法，就会运行这个类中的run()方法，所以这里会执行p1.run()
    # 启动进程
    p1.start()
    p2.start()
    # 输出p1和p2进程的执行状态，如果真正进行，返回True,否则返回False
    print("p1.is_alive=%s" % p1.is_alive())
    print("p2.is_alive=%s" % p2.is_alive())
    # 输出p1和p2进程的别名和PID
    print("p1.name=%s" % p1.name)
    print("p1.pid=%s" % p1.pid)
    print("p2.name=%s" % p2.name)
    print("p2.pid=%s" % p2.pid)
    print("------等待子进程-------")
    # 等待p1进程结束
    p1.join()
    # 等待p2进程结束
    p2.join()
    print("------父进程执行结束-------")


# 4、使用进程池Pool创建进程
# Pool类的常用方法：
# apply_async(func[,args[,kwds]]):使用非阻塞方式调用func()函数（并行执行，阻塞方式必须等待上一个进程退出才能执行下一个进程），args为传递给func()函数的参数列表，　kwds为传递给func()函数的关键字参数列表
# apply(func[,args[,kwds]]):使用阻塞方式调用func()函数
# close():关闭Pool，使其不再接受新的任务
# terminate():不管任务是否完成，立即终止
# join():主进程阻塞，等待子进程的退出，必须在close或terminate之后使用
def task(name):
    print('子进程（%s）执行task %s ...' % (os.getpid(), name))
    # 休眠1秒
    time.sleep(1)


if __name__ == '__main__':
    print('父进程（%s）.' % os.getpid())
    # 定义一个进程池，最大进程数3
    p = Pool(3)
    # 从0开始循环10次
    for i in range(10):
        # 使用非阻塞方式调用task()函数
        p.apply_async(task, args=(i,))
    print('等待所有子进程结束...')
    # 关闭进程池，关闭后p不再接收新的请求
    p.close()
    # 主进程阻塞，等待子进程的退出，必须在close或terminate之后使用
    p.join()
    print('所有子进程结束.')
